home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
CUGUK
/
COMMS
/
C022.ZIP
/
MORE.C
< prev
next >
Wrap
Text File
|
1990-01-31
|
31KB
|
1,276 lines
/************************************************************************
* C Users Group (U.K) C Source Code Library File 00000000 *
* Inquiries to: M. Houston, 36 Whetstone Clo. Farquhar Rd. *
* Edgbaston, Birmingham B15 2QN ENGLAND *
************************************************************************
* File name: more.c *
* Program : more *
* *
* Source : Adrian Godwin, with some code from Minix's editor *
* Purpose : file or pipe paginator *
* Changes : *
* *
* 12.06.88 A Godwin - release to CUG *
************************************************************************/
/* An implementation of the Berkeley 'more' utility for MINIX.
*
* Most of the paging commands are supported ('v' gets mined, not vi, and
* doesn't go to the current line number), but only window size, line number
* search and help options are supported on the command line in this initial
* version.
* MINIX terminal i/o escape codes are built in, rather than using TERMCAP.
* Two additional paging commands, 'e' and 'w', perform screen repaint and
* current screen write-out to a file, respectively.
*
* Search routines are those by Michiel Huisjes, as used in mined.
* - these are much smaller than those in the regex library routiines,
* and have all the functionality required here.
*
* The MINIX library showed some bugs, which need fixing before this will
* work properly:
*
* fseek() & getc() handle the buffer count wrongly : this causes
* an incorrect value to be returned by ftell(). The effect is to
* generate a blank line if an attempt is made to return to the
* starting point of a search (also when a search fails).
* system() does not exist.
* puts() is defined in stdio.h as fputs(s,stdout). It should be a
* separate function, appending a newline character. To avoid problems
* where this is not fixed, fputs() and putchar('\n') are used.
*
* The assembler asld needs a largish (78K) temporary file to compile this:
* use the -T. flag to avoid using /tmp.
* Reduce the program's stack size to about 10000 with chmem.
* The help screen displayed when the interactive command 'h' is used is
* actually obtained from a help file, rather than built into the program.
* This file is normally /usr/lib/more.hlp - defined by the global string
* 'helpfile'.
*
*/
#define VERSION "1.1 (12.06.88)"
#include <stdio.h>
#include <sgtty.h>
#include <signal.h>
#include <stat.h>
#define TRUE 1
#define FALSE 0
#define LONGSCRN 25 /* longest expected screen length */
#define LINELEN 80 /* longest expected line length */
#define AGAIN -32000 /* display the same file again */
#define COLON 0x8000 /* command letter modified by ':' */
#define BLOCK_SIZE 1024 /* used to define size of compiled REGEX */
#define BACK ('\\') /* special character escape */
#define KILL ('@') /* should come from TIOCGETC */
#define ERASE ('\b')
/* default options (may get changed by command line or commands */
char *spattern = ""; /* search pattern */
int firstline = 0; /* first line to read in file */
int window = LONGSCRN-2; /* new lines to scroll per screen */
int scroll = 11; /* lines in a scroll */
int disp_cons = TRUE; /* display control chars as ^ch */
/* globals */
/* This string defines where the help file (displayed when h is given
* at the interactive prompt) is found.
*/
char *helpfile = "/usr/lib/more.hlp";
/* This string defines the text editor invoked when v is given at the
* interactive prompt. Mined cannot be informed which line to open
* the edit at.
*/
char *editor = "mined";
int input_fd = 0; /* more's control stream */
int quit = FALSE;/* true if SIGQUIT received */
int repeat = 1; /* repeat count for a few commands */
char *filename = NULL; /* name of current file */
FILE *filep = NULL; /* current file pointer */
int lineno = 0; /* current line number */
int eoflag = FALSE;/* read end-of-file */
long filelength = -1L; /* length of current file */
long searchfrom = 0L; /* fseek() where last search started */
int searchline = 0; /* lineno where last search started */
char inline[LINELEN]; /* character input line buffer */
char *sline[LONGSCRN]; /* pointers to a screenfull of line buffers */
int linesused = 0; /* number of linebuffers with any contents */
extern FILE *fopen();
extern long ftell();
extern char *fgets();
/* screen control settings - could be read from termcap, if there was one */
char *pclear = "\r\033~0"; /* clear current line (and all after it) */
char *invon = "\033zp"; /* inverse video */
char *invoff = "\033z\07"; /* normal video */
int scrlen = LONGSCRN; /* lines in a screenfull (window size) */
int scrwid = LINELEN; /* columns in a screenwidth */
panic(s,t)
char *s,*t;
{
fprintf(stderr,"more failed : %s%s\n", s, t);
cleanup();
}
usage(s,t)
char *s,*t;
{
fprintf(stderr,"more : %s %s\n", s, t);
fprintf(stderr,"usage : more [-n] [+linenumber] [+/pattern] [file ...]\n");
cleanup();
}
main(argc,argv)
int argc;
char *argv[];
{
int cmd = 0; /* first operation on file */
char *errbuf, *scrbuf, *malloc();
char **first, **last; /* first and last file args in cmd line */
int step,i;
/* setup the io */
if ((errbuf = malloc(BUFSIZ)) == NULL)
panic("no memory for buffers","");
setbuf(stderr,errbuf);
perprintf(stdout);
perprintf(stderr);
if (!isatty(0)) /* standard input not a tty - open /dev/tty */
open_device();
raw_mode(TRUE);
/* allocate buffers for screenfull of text */
if ((scrbuf = malloc((scrwid+1) * LONGSCRN)) == NULL)
panic("no memory for line buffers","");
for (i = 0; i < LONGSCRN; i++) {
sline[i] = scrbuf;
scrbuf += scrwid+1;
}
/* unbundle the options */
while( argv++, --argc ) {
if (**argv == '-') {
/* help argument */
if ((*argv)[1] == 'h')
usage("version ",VERSION);
/* stdin argument */
else if ((*argv)[1] == '\0')
break;
/* lines-per-page argument */
else
window = narg((*argv)+1);
}
else if (**argv == '+') {
/* starting pattern argument */
if ((*argv)[1] == '/') {
spattern = (*argv)+2;
cmd = 'n';
}
/* starting line argument */
else {
repeat = narg((*argv)+1);
cmd = 's';
}
}
else
break; /* no more flags - do the files */
}
first = argv;
last = argv + argc - 1;
while (*argv != NULL) {
step = more(*argv,cmd);
/* step returned as :
+ve : skip forwards
-ve : skip backwards
AGAIN : skip to start of this file
*/
if (step == AGAIN)
step = 0;
if (argv == last && step == 1)
break; /* off end of file list */
if (!eoflag)
printf("\nskipping ...\n");
argv += step;
if (argv > last) { /* clip to limits of list */
argv = last;
printf("skipping back to file %s ..\n\n",*argv);
}
else if (argv < first) {
argv = first;
printf("skipping to file %s ..\n\n",*argv);
}
else
printf("starting file %s\n\n",*argv);
printf("%s%s next file : %s %s",pclear,invon,*argv,invoff);
cmd = getcom();
}
if (argc == 0) { /* no files specified - do standard input */
if (isatty(0))
usage("no input stream","");
else
more("-",cmd);
}
cleanup();
}
/* narg() - extract a decimal argument from the string or fatal error. */
narg(s)
char *s;
{
int res;
if (s == NULL || sscanf(s, "%d", &res) != 1)
usage("bad numeric argument ",s == NULL ? "" : s);
return res;
}
/* Routine to open terminal when more is used in a pipeline. */
open_device()
{
if ((input_fd = open("/dev/tty", 0)) < 0)
panic("Cannot open /dev/tty for read","");
}
getcc()
{
char c = 0;
while (read(input_fd, &c, 1) != 1 && quit == FALSE)
;/* wait for an input char, not a SIG. */
return c & 0377;
}
getcom()
{
int c = '0';
int arg = FALSE;
static int lastrepeat = 1;
lastrepeat = repeat;
repeat = 0;
while ( (c=getcc()) >= '0' && c <= '9') {
repeat = (repeat * 10) + (c - '0');
arg = TRUE;
}
if (arg == FALSE) repeat = 1; /* default repeat count */
if (c == ':') /* command prefix */
c = getcc() | COLON;
if (c == '.')
repeat = lastrepeat;
return c;
}
char *getstring(prompt,buf)
char *prompt, *buf;
{
int incount = 0;
int c;
char erase = ERASE;
char kill = KILL;
struct sgttyb sbuf;
printf("%s%s",pclear,prompt);
if (ioctl(input_fd,TIOCGETP,&sbuf) == 0) {
erase = sbuf.sg_erase;
kill = sbuf.sg_kill;
}
while (quit == FALSE && (c = getcc()) != '\n') {
if (c == erase) {
if (incount--)
printf("\b \b");
else {
printf("%s%s",invoff,pclear);
buf[0] = '\0';
return NULL;
}
}
else if (c == kill) {
printf("%c\n%s",kill,prompt);
incount = 0;
}
else {
if (c == BACK) {
putchar(c);
fflush(stdout);
c = getcc();
}
if (incount >= LINELEN)
putchar('\07');
else {
putchar(c);
buf[incount++] = c;
}
}
fflush(stdout);
}
printf("%s",invoff);
buf[incount] = '\0';
return (quit ? NULL : buf);
}
int catch()
{
signal(SIGQUIT, catch);
quit = TRUE;
}
int icatch()
{
signal(SIGINT, icatch);
howmuch();
}
int cleanup()
{
printf("%s\n",pclear);
raw_mode(FALSE);
_cleanup();
exit(0);
}
howmuch()
{
int percent = -1;
if (filelength > 0L)
percent = ((ftell(filep) * 100L)/filelength);
if (percent < 0 && !eoflag)
printf("%s%s --More-- %s",pclear,invon,invoff);
else if (!eoflag)
printf("%s%s --More-- (%d\%)%s",pclear, invon,percent, invoff);
fflush(stdout);
}
/*
* Set and reset tty into CBREAK or old mode
* according to argument `state'.
*/
int raw_mode(state)
int state;
{
static int saved = FALSE;
static struct sgttyb old_tty;
static struct sgttyb new_tty;
if (state == FALSE) {
if (saved == FALSE) /* ever set rawmode on ? */
return;
saved = FALSE; /* force next set_raw to get settings */
ioctl(input_fd, TIOCSETP, &old_tty);
return;
}
saved = TRUE;
/* Save old tty settings */
ioctl(input_fd, TIOCGETP, &old_tty);
/* Set tty to CBREAK mode */
ioctl(input_fd, TIOCGETP, &new_tty);
new_tty.sg_flags |= CBREAK;
new_tty.sg_flags &= ~ECHO;
ioctl(input_fd, TIOCSETP, &new_tty);
/* Catch quit signal (sets quit flag) and interrupt signal */
signal(SIGQUIT, catch);
signal(SIGINT, icatch);
}
more(name,cmd)
char *name;
int cmd;
{
int res;
struct stat sbuf;
eoflag = TRUE;
filelength = -1;
if (*name == '-' && *(name+1) == '\0') {
if (isatty(0)) {
printf("%sCannot read both input and commands from stdin\n",
pclear);
return 1;
}
filep = stdin;
filename = "standard input";
}
else {
if ((filep = fopen(name,"r")) == NULL) {
printf("%scannot open file %s\n",pclear,name);
return 1;
}
filename = name;
if (fstat(filep->_fd, &sbuf) == 0)
filelength = sbuf.st_size;
if (filelength < 1L)
filelength = 1L;
}
lineno = searchline = linesused = 0;
eoflag = FALSE;
searchfrom = 0L;
/* do first & subsequent operations, until file exhausted */
while ((res = do_command(cmd)) == 0)
cmd = getcom();
if (filep != stdin)
fclose(filep);
return res; /* main() uses this for stepping thro' command line */
}
do_command(cmd)
int cmd;
{
static int lastcmd = 0;
char ibuf[LINELEN];
char pbuf[LINELEN];
FILE *fp;
int c,count;
fputs(pclear,stdout);
if (quit) { /* command quitted before it's executed */
quit = FALSE;
howmuch();
return 0;
}
if (cmd == '.') {
if (lastcmd == '/') /* repeat search command = 'n' */
cmd = 'n';
else cmd = lastcmd; /* repeat any other command */
}
quit = FALSE;
lastcmd = cmd;
switch (cmd) {
case '\n':
case '\r':
return lines(1);
case ' ':
case 0:
return lines(repeat > 1 ? repeat : window);
case '=':
printf("%s%sline %d %s",pclear,invon,lineno,invoff);
return 0;
case '/':
if ( (spattern = getstring("/",ibuf)) == NULL) {
howmuch();
return 0;
}
return search(spattern);
case '\'':
if (filep != stdin) {
fseek(filep,searchfrom,0);
lineno = searchline;
return lines(window);
}
return 0;
case '!':
if (getstring("!",ibuf) == NULL) {
howmuch();
return 0;
}
go_exec(ibuf);
return 0;
case 'v': /* should be vi, but mined will have to do */
sprintf(ibuf,"%s %s",editor,filename);
go_exec(ibuf);
return 0;
case 'd':
case 4:
if (repeat > 1)
scroll = repeat;
return lines(scroll);
case 'e':
case 5:
repaint();
return 0;
case 'f':
skip(repeat * window);
return lines(window-3);
case 'f'|COLON:
printf("%s%sfile '%s', line %d %s",
pclear,invon,filename,lineno,invoff);
return 0;
case 'h':
if ((fp = fopen(helpfile,"r")) == NULL) {
printf("%s%sno help available - missing %s%s\n",
pclear,invon,helpfile,invoff);
return 0;
}
/* copy out the help file */
putchar('\n');
while ((c = getc(fp)) != EOF)
putchar(c);
fclose(fp);
fflush(stdout);
getcc();
repaint();
return 0;
case 'n':
return search(spattern); /* search again */
case 'n'|COLON:
return (repeat);
case 'p'|COLON:
if (filep == stdin) {
printf("\07");
return 0;
}
if (repeat == 1 && lineno > window)
return AGAIN; /* start of file */
else
return -repeat; /* skip back */
case 'q':
case 'Q':
case 'q'|COLON:
case 'Q'|COLON:
cleanup();
exit(0);
case 's':
skip(repeat);
return lines(window-3);
case 'w':
count = (repeat > 1 ? repeat : window);
sprintf(pbuf,"write %d lines to file: ",count);
if (getstring(pbuf,ibuf) == NULL) {
howmuch();
return 0;
}
return wrt_lines(ibuf,count);
case 'z':
if (repeat > 1)
window = repeat;
return lines(window);
}
printf("\\%o not implemented",cmd);
return 0;
}
/* repaint all the lines back on the screen */
repaint()
{
int i;
putchar('\n');
for (i = linesused; i > 0; i--) {
fputs(sline[i-1],stdout);
putchar('\n');
}
fflush(stdout);
}
/* get the next line (or partial line) from the input file */
char *getline()
{
char *p;
int i, ch;
/* rotate buffer pointers so latest lines are at the top */
p = sline[LONGSCRN-1];
for (i = LONGSCRN-1; i > 0; i--)
sline[i] = sline[i-1];
sline[0] = p;
/* indicate how many line buffers are available */
if (linesused < LONGSCRN)
linesused++;
else
linesused = LONGSCRN;
/* read a line, or a screen width, into the current line buffer */
if (feof(filep) || quit == TRUE)
return NULL;
for (p = *sline, i = 0; i < scrwid; i++) {
switch(ch = getc(filep)) {
case '\n':
case EOF:
ch = '\0';
lineno++; /* real line end - terminate line */
i = scrwid; /* force end of read */
break;
case '\t':
i += 7 - (i % 8); /* count tabs to stop line ... */
if (i >= scrwid) /* .. overflowing uncounted. */
ch = ' ';
break;
default: /* display control chars as ^C */
if (ch < ' ') {
if (disp_cons) {
if (++i < scrwid) {
*p++ = '^';
ch += '@';
}
else
ch = '^';
}
}
}
*p++ = ch;
}
*p++ = '\0'; /* terminate counted-out line */
return *sline;
}
skip(n)
int n;
{
if (n < 1)
return;
printf("\nskipping ... \n\n");
while (n-- > 0 && getline() != NULL)
;
}
lines(n)
int n;
{
char *lread;
if (eoflag) /* don't read past end of file */
return 1;
fputs(pclear,stdout);
while (n-- > 0 && (lread = getline()) != NULL) {
fputs(lread,stdout);
putchar('\n');
}
if (lread == NULL && quit == FALSE) eoflag = TRUE;
howmuch(); /* display --More-- (%nn) prompt */
return (eoflag ? 1 : 0);
}
/* call system(), tidying up the screen around it. */
go_exec(cmd)
char *cmd;
{
int res;
printf("\n");
raw_mode(FALSE);
res = system(cmd);
raw_mode(TRUE);
printf("\n\n");
howmuch();
return res;
}
/* write part of the file out to another - 'count' lines, starting
* with those in the window.
*/
wrt_lines(name,count)
char *name;
int count;
{
int bcount,ret = 0;
FILE *fp;
char *lread = ""; /* not NULL */
if ((fp = fopen(name, "w")) == NULL) {
printf("%s%scannot create file %s%s",pclear,invon,name,invoff);
return 0;
}
fputs(pclear,stdout);
bcount = (linesused < window ? linesused : window);
bcount = (bcount < count ? bcount : count);
count -= bcount;
/* write out as much as possible from the line buffers */
while (bcount-- > 0)
fprintf(fp, "%s\n", sline[bcount]);
/* write out the remainder by reading (and displaying) the file */
while (count-- > 0 && !eoflag && (lread = getline()) != NULL) {
fputs(lread,stdout); /* write the line to the screen */
putchar('\n');
fputs(lread,fp); /* write the line to the file */
putc('\n',fp);
}
if (lread == NULL && quit == FALSE) eoflag = TRUE;
fclose(fp);
howmuch();
/* if we hit the end of the file, wait unless it's stdin. */
if (filep == stdin && eoflag)
return 1;
else
return 0;
}
/* ================================================================ *
* Search Routines *
* ================================================================ *
*
* These routines are poached directly from MINED source with only minor
* modifications (simplifications for MORE). All credits to the original
* writer, Michiel Huisjes.
* The library regex routines were not used, because they are much more
* general than these, and are bigger than the whole of this utility!
*
* A regular expression consists of a sequence of:
* 1. A normal character matching that character.
* 2. A . matching any character.
* 3. A ^ matching the begin of a line.
* 4. A $ (as last character of the pattern) mathing the end of a line.
* 5. A \<character> matching <character>.
* 6. A number of characters enclosed in [] pairs matching any of these
* characters. A list of characters can be indicated by a '-'. So
* [a-z] matches any letter of the alphabet. If the first character
* after the '[' is a '^' then the set is negated (matching none of
* the characters).
* A ']', '^' or '-' can be escaped by putting a '\' in front of it.
* 7. If one of the expressions as described in 1-6 is followed by a
* '*' than that expressions matches a sequence of 0 or more of
* that expression.
*/
/* Expression definitions */
#define NO_MATCH 0
#define MATCH 1
#define REG_ERROR 2
#define BEGIN_LINE (2 * REG_ERROR)
#define END_LINE (2 * BEGIN_LINE)
/*
* The regex structure. Status can be any of 0, BEGIN_LINE or REG_ERROR. In
* the last case, the result.err_mess field is assigned. Start_ptr and end_ptr
* point to the match found. For more details see the documentation file.
*/
struct regex {
union {
char *err_mess;
int *expression;
} result;
char status;
char *start_ptr;
char *end_ptr;
};
typedef struct regex REGEX;
/* NULL definitions */
#define NIL_PTR ((char *) 0)
#define NIL_REG ((REGEX *) 0)
#define NIL_INT ((int *) 0)
/* Opcodes for characters */
#define NORMAL 0x0200
#define DOT 0x0400
#define EOLN 0x0800
#define STAR 0x1000
#define BRACKET 0x2000
#define NEGATE 0x0100
#define DONE 0x4000
/* Mask for opcodes and characters */
#define LOW_BYTE 0x00FF
#define HIGH_BYTE 0xFF00
/* Previous is the contents of the previous address (ptr) points to */
#define previous(ptr) (*((ptr) - 1))
/* Buffer to store outcome of compilation */
int exp_buffer[BLOCK_SIZE];
/* Errors often used */
char *too_long = "Regular expression too long";
/*
* Reg_error() is called by compile() is something went wrong. It set the
* status of the structure to error, and assigns the error field of the union.
*/
#define reg_error(str) program->status = REG_ERROR, \
program->result.err_mess = (str)
/*
* Compile compiles the pattern into a more comprehensible form and returns a
* REGEX structure. If something went wrong, the status field of the structure
* is set to REG_ERROR and an error message is set into the err_mess field of
* the union. If all went well the expression is saved and the expression
* pointer is set to the saved (and compiled) expression.
*/
compile(pattern, program)
register char *pattern; /* Pointer to pattern */
REGEX *program;
{
register int *expression = exp_buffer;
int *prev_char; /* Pointer to previous compiled atom */
int *acct_field; /* Pointer to last BRACKET start */
int negate; /* Negate flag for BRACKET */
char low_char; /* Index for chars in BRACKET */
char c;
program->result.expression = expression;
/* Check for begin of line */
if (*pattern == '^') {
program->status = BEGIN_LINE;
pattern++;
}
else {
program->status = 0;
/* If the first character is a '*' we have to assign it here. */
if (*pattern == '*') {
*expression++ = '*' + NORMAL;
pattern++;
}
}
for (; ;) {
switch (c = *pattern++) {
case '.' :
*expression++ = DOT;
break;
case '$' :
/*
* Only means EOLN if it is the last char of the pattern
*/
if (*pattern == '\0') {
*expression++ = EOLN | DONE;
program->status |= END_LINE;
return;
}
else
*expression++ = NORMAL + '$';
break;
case '\0' :
*expression++ = DONE;
return;
case '\\' :
/* If last char, it must! mean a normal '\' */
if (*pattern == '\0')
*expression++ = NORMAL + '\\';
else
*expression++ = NORMAL + *pattern++;
break;
case '*' :
/*
* If the previous expression was a [] find out the
* begin of the list, and adjust the opcode.
*/
prev_char = expression - 1;
if (*prev_char & BRACKET)
*(expression - (*acct_field & LOW_BYTE))|= STAR;
else
*prev_char |= STAR;
break;
case '[' :
/*
* First field in expression gives information about
* the list.
* The opcode consists of BRACKET and if necessary
* NEGATE to indicate that the list should be negated
* and/or STAR to indicate a number of sequence of this
* list.
* The lower byte contains the length of the list.
*/
acct_field = expression++;
if (*pattern == '^') { /* List must be negated */
pattern++;
negate = TRUE;
}
else
negate = FALSE;
while (*pattern != ']') {
if (*pattern == '\0') {
reg_error("Missing ]");
return;
}
if (*pattern == '\\')
pattern++;
*expression++ = *pattern++;
if (*pattern == '-') {
/* Make list of chars */
low_char = previous(pattern);
pattern++; /* Skip '-' */
if (low_char++ > *pattern) {
reg_error("Bad range in [a-z]");
return;
}
/* Build list */
while (low_char <= *pattern)
*expression++ = low_char++;
pattern++;
}
if (expression >= &exp_buffer[BLOCK_SIZE]) {
reg_error(too_long);
return;
}
}
pattern++; /* Skip ']' */
/* Assign length of list in acct field */
if ((*acct_field = (expression - acct_field)) == 1) {
reg_error("Empty []");
return;
}
/* Assign negate and bracket field */
*acct_field |= BRACKET;
if (negate == TRUE)
*acct_field |= NEGATE;
/*
* Add BRACKET to opcode of last char in field because
* a '*' may be following the list.
*/
previous(expression) |= BRACKET;
break;
default :
*expression++ = c + NORMAL;
}
if (expression == &exp_buffer[BLOCK_SIZE]) {
reg_error(too_long);
return;
}
}
/* NOTREACHED */
}
/* search the file for a regular expression. It may be :
*
* a command line argument } (string = regular expression)
* a user-entered string }
* a repeat of the last string (string = NULL)
*/
search(string)
char *string;
{
char *text,*match();
static REGEX program;
int loops,i,ret;
if (string != NULL) {
compile( string, &program);
if (program.status == REG_ERROR) {
printf("%s%s %s%s",
pclear,invon,program.result.err_mess,invoff);
program.result.expression = NIL_INT;
return 0;
}
}
if (program.result.expression == NIL_INT) {
printf("%s%s No previous search pattern%s",pclear,invon,invoff);
return 0;
}
if (string == spattern)
spattern = NULL; /* global pattern now compiled */
/* save current position */
searchfrom = ftell(filep);
searchline = lineno;
loops = repeat;
while ( (text = getline()) != NULL && quit == FALSE) {
if (line_check(&program, text) == MATCH) {
if (--loops > 0) /* stop only when repeat exceeded */
continue;
/* line found - update screen */
printf("\nskipping ... \n\n");
for (i = 2; i >= 0; i--)
if (i < linesused)
printf("%s\n",sline[i]);
ret = lines(window-6);
/* only slip off the bottom of the file if it's stdin. */
if (filep == stdin)
return ret;
else
return 0;
}
}
/* string not found : print error and return to old position */
if (quit)
howmuch();
else
printf("%s%s string not found %s",pclear,invon,invoff);
if (filep != stdin) {
fseek(filep,searchfrom,0);
lineno = searchline;
}
return 0;
}
/*
* Line_check() checks the line (or rather string) for a match.
* It scans through the whole string
* until a match is found, or the end of the string is reached.
*/
line_check(program, string)
register REGEX *program;
char *string;
{
register char *textp = string;
/* Assign start_ptr field. We might find a match right away! */
program->start_ptr = textp;
/* If the match must be anchored, just check the string. */
if (program->status & BEGIN_LINE)
return check_string(program, string, NIL_INT);
/* Move through the string until the end of is found */
while (quit == FALSE && *textp != '\0') {
program->start_ptr = textp;
if (check_string(program, textp, NIL_INT))
return MATCH;
if (*textp == '\n')
break;
textp++;
}
return NO_MATCH;
}
/*
* Check() checks of a match can be found in the given string. Whenever a STAR
* is found during matching, then the begin position of the string is marked
* and the maximum number of matches is performed. Then the function star()
* is called which starts to finish the match from this position of the string
* (and expression). Check() return MATCH for a match, NO_MATCH is the string
* couldn't be matched or REG_ERROR for an illegal opcode in expression.
*/
check_string(program, string, expression)
REGEX *program;
register char *string;
int *expression;
{
register int opcode; /* Holds opcode of next expr. atom */
char c; /* Char that must be matched */
char *mark; /* For marking position */
int star_fl; /* A star has been born */
if (expression == NIL_INT)
expression = program->result.expression;
/* Loop until end of string or end of expression */
while (quit == FALSE && !(*expression & DONE) &&
*string != '\0' && *string != '\n') {
c = *expression & LOW_BYTE; /* Extract match char */
opcode = *expression & HIGH_BYTE; /* Extract opcode */
if (star_fl = (opcode & STAR)) { /* Check star occurrence */
opcode &= ~STAR; /* Strip opcode */
mark = string; /* Mark current position */
}
expression++; /* Increment expr. */
switch (opcode) {
case NORMAL :
if (star_fl)
while (*string++ == c) /* Skip all matches */
;
else if (*string++ != c)
return NO_MATCH;
break;
case DOT :
string++;
if (star_fl) /* Skip to eoln */
while (*string != '\0' && *string++ != '\n')
;
break;
case NEGATE | BRACKET:
case BRACKET :
if (star_fl)
while (in_list(expression, *string++, c, opcode)
== MATCH)
;
else if (in_list(expression, *string++, c, opcode) == NO_MATCH)
return NO_MATCH;
expression += c - 1; /* Add length of list */
break;
default :
panic("Corrupted program in check_string()","");
}
if (star_fl)
return star(program, mark, string, expression);
}
if (*expression & DONE) {
program->end_ptr = string; /* Match ends here */
/*
* We might have found a match. The last thing to do is check
* whether a '$' was given at the end of the expression, or
* the match was found on a null string. (E.g. [a-z]* always
* matches) unless a ^ or $ was included in the pattern.
*/
if ((*expression & EOLN) && *string != '\n' && *string != '\0')
return NO_MATCH;
if (string == program->start_ptr && !(program->status & BEGIN_LINE)
&& !(*expression & EOLN))
return NO_MATCH;
return MATCH;
}
return NO_MATCH;
}
/*
* Star() calls check_string() to find out the longest match possible.
* It searches backwards until the (in check_string()) marked position
* is reached, or a match is found.
*/
star(program, end_position, string, expression)
REGEX *program;
register char *end_position;
register char *string;
int *expression;
{
do {
string--;
if (check_string(program, string, expression))
return MATCH;
} while (string != end_position);
return NO_MATCH;
}
/*
* In_list() checks if the given character is in the list of []. If it is
* it returns MATCH. if it isn't it returns NO_MATCH. These returns values
* are reversed when the NEGATE field in the opcode is present.
*/
in_list(list, c, list_length, opcode)
register int *list;
char c;
register int list_length;
int opcode;
{
if (c == '\0' || c == '\n') /* End of string, never matches */
return NO_MATCH;
while (list_length-- > 1) { /* > 1, don't check acct_field */
if ((*list & LOW_BYTE) == c)
return (opcode & NEGATE) ? NO_MATCH : MATCH;
list++;
}
return (opcode & NEGATE) ? MATCH : NO_MATCH;
}